﻿//OrderTraceSerializer.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Storages
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;

	using Ecng.Common;
	using Ecng.Serialization;

	using StockSharp.BusinessEntities;

	class OrderTraceMetaInfo : MetaInfo<OrderTraceMetaInfo>
	{
		public OrderTraceMetaInfo(DateTime date)
			: base(date)
		{
			FirstOrderId = -1;
		}

		public long FirstOrderId { get; set; }
		public long FirstTradeId { get; set; }
		public long FirstOrderTransactionId { get; set; }

		public long PrevOrderId { get; set; }
		public long PrevTradeId { get; set; }
		public long PrevOrderTransactionId { get; set; }

		public DateTime FirstLocalTime { get; set; }
		public DateTime PrevLocalTime { get; set; }

		public override void Write(Stream s)
		{
			base.Write(s);

			s.Write(FirstOrderId);
			s.Write(FirstTradeId);
			s.Write(FirstOrderTransactionId);
			s.Write(PrevOrderId);
			s.Write(PrevTradeId);
			s.Write(PrevOrderTransactionId);
			s.Write(FirstPrice);
			s.Write(LastPrice);
			s.Write(FirstLocalTime);
			s.Write(PrevLocalTime);
		}

		public override void Read(Stream s)
		{
			base.Read(s);
			FirstOrderId = s.Read<long>();
			FirstTradeId = s.Read<long>();
			FirstOrderTransactionId = s.Read<long>();
			PrevOrderId = s.Read<long>();
			PrevTradeId = s.Read<long>();
			PrevOrderTransactionId = s.Read<long>();
			FirstPrice = s.Read<decimal>();
			LastPrice = s.Read<decimal>();
			FirstLocalTime = s.Read<DateTime>();
			PrevLocalTime = s.Read<DateTime>();
		}

		protected override void CopyFrom(OrderTraceMetaInfo src)
		{
			base.CopyFrom(src);

			FirstOrderId = src.FirstOrderId;
			FirstTradeId = src.FirstTradeId;
			FirstOrderTransactionId = src.FirstOrderTransactionId;

			PrevOrderId = src.PrevOrderId;
			PrevTradeId = src.PrevTradeId;
			PrevOrderTransactionId = src.PrevOrderTransactionId;

			FirstPrice = src.FirstPrice;
			LastPrice = src.LastPrice;
		}
	}

	class OrderTraceSerializer : MarketDataSerializer<OrderTraceItem, OrderTraceMetaInfo>
	{
		public OrderTraceSerializer(Security security)
			: base(security, 200)
		{
		}

		protected override void OnSave(List<bool> bits, IEnumerable<OrderTraceItem> items, OrderTraceMetaInfo metaInfo)
		{
			if (metaInfo.IsEmpty())
			{
				var firstItem = items.First();

				metaInfo.FirstOrderId = metaInfo.PrevOrderId = firstItem.Order.Id;
				metaInfo.FirstPrice = metaInfo.LastPrice = firstItem.Order.Price;
				metaInfo.FirstOrderTransactionId = metaInfo.PrevOrderTransactionId = firstItem.Order.TransactionId;

				metaInfo.FirstLocalTime = metaInfo.PrevLocalTime = firstItem.LocalTime.Truncate();
			}

			bits.SerializeInt(items.Count());

			foreach (var traceItem in items)
			{
				var order = traceItem.Order;
				var trade = traceItem.Trade;

				//if(order.Id <= 0)
				//	throw new ArgumentOutOfRangeException("quotes", order.Id, "Неправильный номер заявки.");

				if (order.TransactionId <= 0)
					throw new ArgumentOutOfRangeException("items", order.Id, "Неправильный номер транзакции заявки.");

				if (order.Price <= 0)
					throw new ArgumentOutOfRangeException("items", order.Price, "Неправильная цена заявки {0}.".Put(order.Id));

				if (order.Volume <= 0)
					throw new ArgumentOutOfRangeException("items", order.Volume, "Неправильный объем заявки {0}.".Put(order.Id));

				if (trade != null)
				{
					if (trade.Id <= 0)
						throw new ArgumentOutOfRangeException("items", trade.Id, "Неправильный номер сделки заявки {0}.".Put(order.Id));

					if (trade.Price <= 0)
						throw new ArgumentOutOfRangeException("items", trade.Price, "Неправильная цена сделки {0} заявки {1}.".Put(trade.Id, order.Id));
				}

				bits.SerializeInt((int)traceItem.Action);

				metaInfo.PrevOrderId = bits.SerializeId(order.Id, metaInfo.PrevOrderId);
				metaInfo.PrevOrderTransactionId = bits.SerializeId(order.TransactionId, metaInfo.PrevOrderTransactionId);

				metaInfo.LastTime = bits.SerializeTime(traceItem.Time, metaInfo.LastTime, "трейсы (биржевое время)");
				metaInfo.PrevLocalTime = bits.SerializeTime(traceItem.LocalTime, metaInfo.PrevLocalTime, "трейсы (локальное время)");

				bits.SerializePrice(order.Price, metaInfo, Security);
				bits.SerializeVolume(order.Volume);
				bits.SerializeVolume(order.Balance);

				bits.Add(order.Direction == OrderDirections.Buy);

				bits.SerializeInt((int)order.State);

				var status = order.Status;

				if (status == null)
					bits.Add(false);
				else
				{
					bits.Add(true);
					bits.SerializeInt((int)status);
				}

				if (trade != null)
				{
					bits.Add(true);

					if (metaInfo.FirstTradeId == 0)
					{
						metaInfo.FirstTradeId = metaInfo.PrevTradeId = trade.Id;
					}

					metaInfo.PrevTradeId = bits.SerializeId(trade.Id, metaInfo.PrevTradeId);

					bits.SerializePrice(trade.Price, metaInfo, Security);
					bits.SerializeVolume(trade.Volume);
				}
				else
					bits.Add(false);

				if (traceItem.Fail != null)
				{
					bits.Add(true);
					bits.SerializeInt(0); // error-code
					bits.SerializeInt(0); // строка
				}
				else
					bits.Add(false);
			}
		}

		public override OrderTraceItem MoveNext(MarketDataEnumerator<OrderTraceItem, OrderTraceMetaInfo> enumerator)
		{
			var reader = enumerator.Reader;
			var metaInfo = enumerator.MetaInfo;

			var action = (OrderTraceActions)reader.DeserializeInt();

			metaInfo.FirstOrderId += reader.DeserializeLong();
			metaInfo.FirstOrderTransactionId += reader.DeserializeLong();

			metaInfo.FirstTime = reader.DeserializeTime(enumerator.Date, metaInfo.FirstTime);
			metaInfo.FirstLocalTime = reader.DeserializeTime(enumerator.Date, metaInfo.FirstLocalTime);

			metaInfo.FirstPrice = reader.DeserializePrice(metaInfo);

			var volume = reader.DeserializeVolume();
			var balance = reader.DeserializeVolume();

			var orderDirection = reader.Read() ? OrderDirections.Buy : OrderDirections.Sell;

			var state = (OrderStates)reader.DeserializeInt();

			var order = new Order
			{
				Security = Security,
				Id = metaInfo.FirstOrderId,
				TransactionId = metaInfo.FirstOrderTransactionId,
				Volume = volume,
				Balance = balance,
				Direction = orderDirection,
				//LastChangeTime = metaInfo.FirstTime,
				Price = metaInfo.FirstPrice,
				State = state,
				IsTrace = true
			};
			if (reader.Read())
			{
				var status = reader.DeserializeInt();
				order.Status = (OrderStatus)status;
				if (status.HasBits(0x01))
					order.ExecutionCondition = OrderExecutionConditions.PutInQueue;
				else if (status.HasBits(0x02))
					order.ExecutionCondition = OrderExecutionConditions.CancelBalance;
			}

			Trade trade;

			if (reader.Read())
			{
				metaInfo.FirstTradeId += reader.DeserializeLong();
				metaInfo.FirstPrice = reader.DeserializePrice(metaInfo);
				var tradeVolume = reader.DeserializeVolume();

				trade = new Trade
				{
					Security = Security,
					Id = metaInfo.FirstTradeId,
					Time = metaInfo.FirstTime,
					Volume = tradeVolume,
					Price = metaInfo.FirstPrice,
					OrderDirection = order.Direction
				};
			}
			else
			{
				trade = null;
			}

			OrderFail fail = null;
			if (reader.Read())
			{
				int errCode = reader.DeserializeInt();
				fail = new OrderFail
				{
					Error = new Exception("Error " + errCode)
				};

				reader.DeserializeInt(); // чтение строки.
			}

			return new OrderTraceItem
			{
				Action = action,
				Order = order,
				Trade = trade,
				Fail = fail,
				Time = metaInfo.FirstTime,
				LocalTime = metaInfo.FirstLocalTime
			};
		}
	}
}